home *** CD-ROM | disk | FTP | other *** search
- /*
- * chart.c
- *
- * Forms Object class: CHART
- *
- * Written by: Mark Overmars
- *
- * Version 2.2 a
- * Date: Jun 21, 1993
- */
-
- #include <malloc.h>
- #include "gl/gl.h"
- #include "gl/device.h"
- #include <sys/types.h>
- #include <math.h>
- #include <string.h>
- #include "forms.h"
-
- #define PI 3.14159265
- #define ARCINC (2.0*PI/3600.0)
-
- /* Object specific information */
-
- typedef struct{
- float val; /* Value of the entry */
- char str[16]; /* Label of the entry */
- int col; /* Color of the entry */
- } ENTRY;
-
- typedef struct{
- int numb; /* Number of entries */
- int maxnumb; /* Maximal number of entries to display */
- ENTRY entries[FL_CHART_MAX+1];/* The entries */
- float min,max; /* The boundaries */
- int autosize; /* Whether the x-axis should be scaled */
- } SPEC;
-
- static void vv(float x, float y)
- { short v[2]; v[0] = (short) x; v[1] = (short) y; v2s(v);}
-
- static void draw_barchart(float x, float y, float w, float h,
- int numb, ENTRY entries[],
- float min, float max, int autosize, int maxnumb)
- /* Draws a bar chart. x,y,w,h is the bounding box, entries the array of
- numb entries and min and max the boundaries. */
- {
- int i;
- float bwidth; /* Width of a bar */
- float zeroh; /* Height of zero value */
- float incr; /* Increment per unit value */
- float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
- incr = h/ (max-min);
- zeroh = y-min * incr;
- if ( -min*incr < lh)
- { incr = (h - lh + min*incr)/(max-min); zeroh = y+lh;}
- if (autosize) bwidth = w/numb; else bwidth = w/maxnumb;
- /* Draw base line */
- fl_color(BLACK);
- bgnline(); vv(x,zeroh); vv(x+w,zeroh); endline();
- if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
- /* Draw the bars */
- for (i=0; i<numb; i++)
- fl_rectbound(x+i*bwidth,zeroh,bwidth+1.0, entries[i].val*incr+1.0,
- entries[i].col);
- /* Draw the labels */
- fl_color(BLACK);
- for (i=0; i<numb; i++)
- fl_drw_text_beside(FL_ALIGN_BOTTOM,x+(i+0.5)*bwidth,zeroh,0.0,0.0,
- BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- }
-
- static void draw_horbarchart(float x, float y, float w, float h,
- int numb, ENTRY entries[],
- float min, float max, int autosize, int maxnumb)
- /* Draws a horizontal bar chart. x,y,w,h is the bounding box, entries the
- array of numb entries and min and max the boundaries. */
- {
- int i;
- float bwidth; /* Width of a bar */
- float zeroh; /* Position of zero value */
- float incr; /* Increment per unit value */
- float lw = 0.0; /* Maximal label width */
- /* Compute maximal label width */
- for (i=0; i<numb; i++)
- if (fl_get_string_width(FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str) > lw)
- lw = fl_get_string_width(FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- if (lw >0.0) lw += 4.0;
- incr = w/ (max-min);
- zeroh = x-min * incr;
- if ( -min*incr < lw)
- { incr = (w - lw + min*incr)/(max-min); zeroh = x+lw;}
- if (autosize) bwidth = h/numb; else bwidth = h/maxnumb;
- /* Draw base line */
- fl_color(BLACK);
- bgnline(); vv(zeroh,y); vv(zeroh,y+h); endline();
- if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
- /* Draw the bars */
- for (i=0; i<numb; i++)
- fl_rectbound(zeroh,y+i*bwidth,entries[numb-i-1].val*incr+1.0,bwidth+1.0,
- entries[numb-i-1].col);
- /* Draw the labels */
- fl_color(BLACK);
- for (i=0; i<numb; i++)
- fl_drw_text_beside(FL_ALIGN_LEFT,zeroh+2.0,y+(i+0.5)*bwidth,0.0,0.0,
- BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[numb-i-1].str);
- }
-
- static void draw_linechart(int type, float x, float y, float w, float h,
- int numb, ENTRY entries[],
- float min, float max, int autosize, int maxnumb)
- /* Draws a line chart. x,y,w,h is the bounding box, entries the array of
- numb entries and min and max the boundaries. */
- {
- int i;
- float ttt;
- float bwidth; /* distance between points */
- float zeroh; /* Height of zero value */
- float incr; /* Increment per unit value */
- float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
- incr = (h-2.0*lh)/ (max-min);
- zeroh = y+lh-min * incr;
- if (autosize) bwidth = w/numb; else bwidth = w/maxnumb;
- /* Draw the values */
- for (i=0; i<numb; i++)
- {
- if (type == FL_SPIKE_CHART)
- {
- bgnline();
- fl_color(entries[i].col);
- vv(x+(i+0.5)*bwidth,zeroh);
- vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
- endline();
- }
- else if (type == FL_LINE_CHART && i != 0)
- {
- bgnline();
- fl_color(entries[i-1].col);
- vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
- vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
- endline();
- }
- else if (type == FL_FILLED_CHART && i != 0)
- {
- bgnpolygon();
- fl_color(entries[i-1].col);
- vv(x+(i-0.5)*bwidth,zeroh);
- vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
- if ((entries[i-1].val > 0.0 && entries[i].val < 0.0) ||
- (entries[i-1].val < 0.0 && entries[i].val > 0.0))
- {
- ttt = entries[i-1].val / (entries[i-1].val - entries[i].val);
- vv(x+(i-0.5+ttt)*bwidth,zeroh);
- endpolygon();
- bgnpolygon();
- vv(x+(i-0.5+ttt)*bwidth,zeroh);
- }
- vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
- vv(x+(i+0.5)*bwidth,zeroh);
- endpolygon();
- bgnline();
- fl_color(BLACK);
- vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
- vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
- endline();
- }
- }
- /* Draw base line */
- fl_color(BLACK);
- bgnline(); vv(x,zeroh); vv(x+w,zeroh); endline();
- /* Draw the labels */
- fl_color(BLACK);
- for (i=0; i<numb; i++)
- if (entries[i].val >= 0.0)
- fl_drw_text_beside(FL_ALIGN_TOP,x+(i+0.5)*bwidth,
- zeroh+entries[i].val*incr,
- 0.0,0.0,BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- else
- fl_drw_text_beside(FL_ALIGN_BOTTOM,x+(i+0.5)*bwidth,
- zeroh+entries[i].val*incr,
- 0.0,0.0,BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- }
-
- static void draw_piechart(float x, float y, float w, float h,
- int numb, ENTRY entries[], int special)
- /* Draws a pie chart. x,y,w,h is the bounding box, entries the array of
- numb entries */
- {
- int i;
- float xc,yc,rad; /* center and radius */
- float tot; /* sum of values */
- float incr; /* increment in angle */
- float curang; /* current angle we are drawing */
- float xl,yl; /* label position */
- float txc,tyc; /* temporary center */
- float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
- /* compute center and radius */
- xc = x+w/2.0; yc = y+h/2.0;
- rad = h/2.0 - lh;
- if (special) { yc -= 0.1*rad; rad = 0.9*rad;}
- /* compute sum of values */
- tot = 0.0;
- for (i=0; i<numb; i++)
- if (entries[i].val > 0.0) tot += entries[i].val;
- if (tot == 0.0) return;
- incr = 3600.0/tot;
- /* Draw the pie */
- curang = 0.0;
- for (i=0; i<numb; i++)
- if (entries[i].val > 0.0)
- {
- txc = xc; tyc = yc;
- /* Correct for special pies */
- if (special && i==0)
- {
- txc += 0.3*rad*cos(ARCINC*(curang+0.5*incr*entries[i].val));
- tyc += 0.3*rad*sin(ARCINC*(curang+0.5*incr*entries[i].val));
- }
- fl_color(entries[i].col);
- arcf(txc,tyc,rad, (int) curang, (int) (curang + incr*entries[i].val));
- fl_color(BLACK);
- arc(txc,tyc,rad, (int) curang, (int) (curang + incr*entries[i].val));
- bgnline();
- vv(txc,tyc); vv(txc+rad*cos(ARCINC*curang),tyc+rad*sin(ARCINC*curang));
- endline();
- curang += 0.5 * incr * entries[i].val;
- /* draw the label */
- xl = txc + 1.1*rad*cos(ARCINC*curang);
- yl = tyc + 1.1*rad*sin(ARCINC*curang);
- if (xl < txc)
- fl_drw_text_beside(FL_ALIGN_LEFT,xl,yl,0.0,0.0,
- BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- else
- fl_drw_text_beside(FL_ALIGN_RIGHT,xl,yl,0.0,0.0,
- BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
- curang += 0.5 * incr * entries[i].val;
- bgnline();
- vv(txc,tyc); vv(txc+rad*cos(ARCINC*curang),tyc+rad*sin(ARCINC*curang));
- endline();
- }
- }
-
- static void draw_chart(FL_OBJECT *ob)
- /* Draws a chart object */
- {
- SPEC *sp = ((SPEC *)(ob->spec));
- float xx,yy,ww,hh;
- float min = sp->min, max = sp->max;
- int i;
- /* Find bounding box */
- xx = ob->x+3.0*FL_CHART_BW;
- yy = ob->y+3.0*FL_CHART_BW;
- ww = ob->w-6.0*FL_CHART_BW;
- hh = ob->h-6.0*FL_CHART_BW;
- /* Find bounds */
- if (min == max)
- {
- min = max = 0.0;
- for (i=0; i<sp->numb; i++)
- {
- if (sp->entries[i].val < min) min = sp->entries[i].val;
- if (sp->entries[i].val > max) max = sp->entries[i].val;
- }
- }
- /* Do the drawing */
- fl_drw_box(ob->boxtype,ob->x,ob->y,ob->w,ob->h,ob->col1,FL_CHART_BW);
- switch (ob->type)
- {
- case FL_BAR_CHART:
- draw_barchart(xx,yy,ww,hh, sp->numb, sp->entries, min, max,
- sp->autosize, sp->maxnumb);
- break;
- case FL_HORBAR_CHART:
- draw_horbarchart(xx,yy,ww,hh, sp->numb, sp->entries, min, max,
- sp->autosize, sp->maxnumb);
- break;
- case FL_PIE_CHART:
- draw_piechart(xx,yy,ww,hh,sp->numb,sp->entries,0);
- break;
- case FL_SPECIALPIE_CHART:
- draw_piechart(xx,yy,ww,hh,sp->numb,sp->entries,1);
- break;
- default:
- draw_linechart(ob->type,xx,yy,ww,hh, sp->numb, sp->entries, min, max,
- sp->autosize, sp->maxnumb);
- break;
- }
- fl_drw_text_beside(ob->align,ob->x,ob->y,ob->w,ob->h,
- ob->lcol,ob->lsize,ob->lstyle,ob->label);
- }
-
- static int handle_chart(FL_OBJECT *ob,int event,float mx,float my,char key)
- /* Handles an event, returns whether value has changed. */
- {
- switch (event)
- {
- case FL_DRAW:
- draw_chart(ob);
- return 0;
- case FL_FREEMEM:
- free(ob->spec);
- return 0;
- }
- return 0;
- }
-
- /*------------------------------*/
-
- FL_OBJECT *fl_create_chart(int type,float x,float y,float w,float h,
- const char *label)
- /* creates an object */
- {
- FL_OBJECT *ob;
- ob = fl_make_object(FL_CHART,type,x,y,w,h,label,handle_chart);
-
- ob->boxtype = FL_CHART_BOXTYPE;
- ob->col1 = FL_CHART_COL1;
- ob->col2 = FL_CHART_COL1;
- ob->align = FL_CHART_ALIGN;
- ob->lcol = FL_CHART_LCOL;
-
- ob->active = FALSE;
-
- ob->spec = (int *) fl_malloc(sizeof(SPEC));
- ((SPEC *)(ob->spec))->numb = 0;
- ((SPEC *)(ob->spec))->maxnumb = FL_CHART_MAX;
- ((SPEC *)(ob->spec))->autosize = TRUE;
- ((SPEC *)(ob->spec))->min = 0.0;
- ((SPEC *)(ob->spec))->max = 0.0;
-
- return ob;
- }
-
- FL_OBJECT *fl_add_chart(int type, float x, float y, float w, float h,
- const char *label)
- /* Adds an object */
- {
- FL_OBJECT *ob;
- ob = fl_create_chart(type,x,y,w,h,label);
- fl_add_object(fl_current_form,ob);
- return ob;
- }
-
- void fl_clear_chart(FL_OBJECT *ob)
- /* Clears the contents of a chart. */
- {
- ((SPEC *)(ob->spec))->numb = 0;
- fl_redraw_object(ob);
- }
-
- void fl_add_chart_value(FL_OBJECT *ob, float val, char str[], int col)
- /* Add an item to the chart. */
- {
- SPEC *sp = ((SPEC *)(ob->spec));
- int i;
- /* Shift entries if required */
- if (sp->numb == sp->maxnumb)
- {
- for (i=0; i<sp->numb-1; i++) sp->entries[i] = sp->entries[i+1];
- sp->numb--;
- }
- /* Fill in the new entry */
- sp->entries[sp->numb].val = val;
- sp->entries[sp->numb].col = col;
- strncpy(sp->entries[sp->numb].str,str,16);
- sp->entries[sp->numb].str[15] = '\0';
- sp->numb++;
- fl_redraw_object(ob);
- }
-
- void fl_insert_chart_value(FL_OBJECT *ob, int index,
- float val, char str[], int col)
- /* Inserts an item before index to the chart. */
- {
- SPEC *sp = ((SPEC *)(ob->spec));
- int i;
- if (index < 1 || index > sp->numb+1) return;
- /* Shift entries */
- for (i=sp->numb; i >= index; i--) sp->entries[i] = sp->entries[i-1];
- if (sp->numb < sp->maxnumb) sp->numb++;
- /* Fill in the new entry */
- sp->entries[index-1].val = val;
- sp->entries[index-1].col = col;
- strncpy(sp->entries[index-1].str,str,16);
- sp->entries[index-1].str[15] = '\0';
- fl_redraw_object(ob);
- }
-
- void fl_replace_chart_value(FL_OBJECT *ob, int index,
- float val, char str[], int col)
- /* Replaces an item in the chart. */
- {
- SPEC *sp = ((SPEC *)(ob->spec));
- if (index < 1 || index > sp->numb) return;
- sp->entries[index-1].val = val;
- sp->entries[index-1].col = col;
- strncpy(sp->entries[index-1].str,str,16);
- sp->entries[index-1].str[15] = '\0';
- fl_redraw_object(ob);
- }
-
- void fl_set_chart_bounds(FL_OBJECT *ob, float min, float max)
- /* Sets the boundaries in the value for the object */
- {
- ((SPEC *)(ob->spec))->min = min;
- ((SPEC *)(ob->spec))->max = max;
- fl_redraw_object(ob);
- }
-
- void fl_set_chart_maxnumb(FL_OBJECT *ob, int maxnumb)
- /* Sets the maximal number of values displayed in the chart */
- {
- SPEC *sp = ((SPEC *)(ob->spec));
- int i;
- /* Fill in the new number */
- if (maxnumb < 0) return;
- if (maxnumb > FL_CHART_MAX)
- sp->maxnumb = FL_CHART_MAX;
- else
- sp->maxnumb = maxnumb;
- /* Shift entries if required */
- if (sp->numb > sp->maxnumb)
- {
- for (i = 0; i<maxnumb; i++)
- sp->entries[i] = sp->entries[i+sp->numb-maxnumb];
- sp->numb = sp->maxnumb;
- fl_redraw_object(ob);
- }
- }
-
- void fl_set_chart_autosize(FL_OBJECT *ob, int autosize)
- /* Sets whether the chart should autosize along the x-axis */
- {
- ((SPEC *)(ob->spec))->autosize = autosize;
- fl_redraw_object(ob);
- }
-
-